#pragma once

#include <iostream>
#include <vector>
#include <unordered_map>
#include <memory>
#include <boost/algorithm/string.hpp>

#include "CommonTools.hpp"
#include "log.hpp"
#include "participle.hpp"

// 构建索引结构：正序索引和逆序索引

namespace index_ns
{
    using namespace Log_ns;

    struct FowardInfo
    {
        // 存储正序索引的资源
        // 1. title
        // 2. content
        // 3. url
        std::string _title;
        std::string _content;
        std::string _url;
        int _fileno;
    };

    struct ReverseInfo
    {
        // 存储每一个单词对应的文件编号，权重
        std::string _word;
        int _fileno;
        int _weight;
    };

    struct InvertedElemPrint
    {
        int _fileno;
        int _weight;
        std::vector<std::string> _words;
    };

    class Index
    {
    private:
        // 根据文件ID返回文件信息
        std::shared_ptr<FowardInfo> Search_File(int fileno)
        {
            if (fileno >= _forward_info.size())
            {
                Log(Info) << "no such file";
                return nullptr;
            }
            return _forward_info[fileno];
        }

        // 根据搜索词来查找所有的相关的文件串
        std::shared_ptr<std::vector<std::shared_ptr<ReverseInfo>>> Search_All_Info(const std::string &query)
        {
            auto iter = _reverse_info.find(query);
            if (iter == _reverse_info.end())
            {
                Log(Info) << "no such query";
                return nullptr;
            }
            return iter->second;
        }

        void Build_Forward_Index(std::vector<std::string> &strs)
        {
            std::shared_ptr<FowardInfo> fwd_ptr(new FowardInfo);
            fwd_ptr->_title = std::move(strs[0]);
            fwd_ptr->_content = std::move(strs[1]);
            fwd_ptr->_url = std::move(strs[2]);

            fwd_ptr->_fileno = _forward_info.size();
            _forward_info.push_back(fwd_ptr);

            // Log(Debug) << "title : " << fwd_ptr->_title;
            // Log(Debug) << "content : "<< fwd_ptr->_content;
            // Log(Debug) << "url : " << fwd_ptr->_url;
        }

        struct Word_cnt
        {
            Word_cnt()
                : title_cnt(0), content_cnt(0)
            {
            }
            int title_cnt;
            int content_cnt;
        };

        void Build_Forward_Index(std::shared_ptr<FowardInfo> fwd_ptr)
        {
            std::shared_ptr<Cutting_text> cut_ptr = Cutting_text::Get_Jieba_Cutting_text();
            // 根据正序索引中的title , content , fileno来建立逆序索引
            std::vector<std::string> title_words, content_words;

            // 1. 对title和content进行分词
            title_words = cut_ptr->Split_Statement(fwd_ptr->_title);
            content_words = cut_ptr->Split_Statement(fwd_ptr->_content);

            // 2. 统计title和content中每个单词出现的频率
            std::unordered_map<std::string, Word_cnt> words;
            for (auto each_title_word : title_words)
            {
                boost::to_lower(each_title_word);
                words[each_title_word].title_cnt++;
            }
            for (auto each_content_word : content_words)
            {
                boost::to_lower(each_content_word);

                words[each_content_word].content_cnt++;
            }

            // 3. 将单词进行汇总,为每个单词分配权重
            for (auto &[word, cnts] : words)
            {
                std::shared_ptr<ReverseInfo> rev_ptr(new ReverseInfo);
                rev_ptr->_fileno = fwd_ptr->_fileno;
                rev_ptr->_word = word;
                rev_ptr->_weight = (cnts.content_cnt - cnts.title_cnt) * 1 + cnts.title_cnt * 10;

                // Log(Debug) << "_fileno : " << rev_ptr->_fileno;
                // Log(Debug) << "_word : "<< rev_ptr->_word;
                // Log(Debug) << "_weight : " << rev_ptr->_weight;

                if (!_reverse_info[word])
                {
                    _reverse_info[word] = std::make_shared<std::vector<std::shared_ptr<ReverseInfo>>>();
                }
                _reverse_info[word]->push_back(rev_ptr);
            }
        }

    public:
        Index(const std::string &root)
            : _root(root)
        {
        }
        ~Index() = default;

        void Build_Index()
        {
            // 建立正序索引 以及 逆序索引
            std::string line;
            std::ifstream ifs(_root, std::ios::in | std::ios::binary);
            if (!ifs.is_open())
            {
                Log(Warning) << "no such file";
                exit(1);
            }

            int total_lines_count = Com_Tools_ns::Get_FileLine_Count(_root);
            Log(Info) << "There are "<<  total_lines_count << " files in total";

            static char sep = '\3';
            int count = 0;
            Com_Tools_ns::Bar show_bar;
            Log(Info) << "Indexing is in progress...";
            while (getline(ifs, line))
            {
                // 对读取上来的字符串进行分割
                std::vector<std::string> strs = Com_Tools_ns::CutString(line, sep);
                if (strs.size() != 3)
                {
                    continue;
                }
                // 进行建立正序索引
                Build_Forward_Index(strs);

                // 建立倒排索引
                Build_Forward_Index(_forward_info.back());
                show_bar(++count, total_lines_count);
            }
            std::cout << "\n";
            Log(Info) << "Index creation successful";
        }

        // 根据搜索词返回对应的json串
        std::string Search_String(const std::string &query)
        {
            // 1. 分词
            // 2. 使用_reverse_info进行逆向索引找到所有相关的文件, 并将所有文件统一整合起来
            // 3. 将所有文件按照权重进行排序
            // 4. 构建响应进行返回
            std::shared_ptr<Cutting_text> cut_ptr = Cutting_text::Get_Jieba_Cutting_text();
            std::vector<std::string> words = cut_ptr->Split_Statement(query);
            // std::vector<std::shared_ptr<ReverseInfo>> related_files;
            std::vector<std::shared_ptr<InvertedElemPrint>> related_files;

            // 补充: 进行文件去重
            std::unordered_map<int, std::shared_ptr<InvertedElemPrint>> tokens_map;

            for (auto &each_word : words)
            {
                auto iter = _reverse_info.find(each_word);
                if (iter == _reverse_info.end())
                {
                    continue;
                }

                auto word_ptr = iter->second;
                for (auto it = word_ptr->begin(); it != word_ptr->end(); it++)
                {
                    int fileno = (*it)->_fileno;
                    auto& same_fileno_ptr = tokens_map[fileno];
                    if(same_fileno_ptr == nullptr) same_fileno_ptr = std::make_shared<InvertedElemPrint>();
                    same_fileno_ptr->_weight += (*it)->_weight;
                    same_fileno_ptr->_words.push_back((*it)->_word);
                    same_fileno_ptr->_fileno = fileno;
                }
                // related_files.insert(related_files.end(), word_ptr->begin(), word_ptr->end());
            }


            for (auto &[fileno, invt] : tokens_map)
            {
                related_files.push_back(invt);
            }

            // std::sort(related_files.begin(), related_files.end(),
            //           [](const std::shared_ptr<ReverseInfo> &x_ptr, const std::shared_ptr<ReverseInfo> &y_ptr)
            //           {
            //               return x_ptr->_weight > y_ptr->_weight;
            //           });


            std::sort(related_files.begin(), related_files.end(),
                      [](const std::shared_ptr<InvertedElemPrint> &x_ptr, const std::shared_ptr<InvertedElemPrint> &y_ptr)
                      {
                          return x_ptr->_weight > y_ptr->_weight;
                      });

            Json::Value all_file;
            Json::StyledWriter writer;
            // for (auto word_ptr : related_files)
            // {
            //     // 1. ReverseInfo中包含:fileno  , weight , word
            //     // 2. 通过fileno来进行正序索引,找到对应的文件
            //     // 3. 将所有文件进行整合

            //     Json::Value file_json = Bulid_Json_For_File(word_ptr->_fileno, word_ptr->_word);
            //     all_file.append(std::move(file_json));
            // }

            int count = 0;
            for (auto invr : related_files)
            {
                // 1. ReverseInfo中包含:fileno  , weight , 
                // 2. 通过fileno来进行正序索引,找到对应的文件
                // 3. 将所有文件进行整合

                Json::Value file_json = Bulid_Json_For_File(invr->_fileno, invr->_words[0]);
                file_json["weight"] = invr->_weight;
                all_file.append(std::move(file_json));
                if(++count == 50) break;
            }

            return writer.write(all_file);
        }

    private:
        std::string _root;
        std::vector<std::shared_ptr<FowardInfo>> _forward_info;
        std::unordered_map<std::string, std::shared_ptr<std::vector<std::shared_ptr<ReverseInfo>>>> _reverse_info;

    private:
        std::string Search_Word(const std::string &text, const std::string &word)
        {
            static int prev_len = 50;
            static int back_len = 60;
            int start, end;
            int pos = text.find(word);
            if (start == std::string::npos)
            {
                Log(Warning);
                return "None";
            }

            start = std::max(0, pos - prev_len);
            end = std::min((int)text.size() - 1, pos + back_len);

            if (start > end)
            {
                Log(Warning);
                return "None";
            }
            return text.substr(start, end - start);
        }

        Json::Value Bulid_Json_For_File(int fileno, const std::string word)
        {
            Json::Value file_json;
            std::shared_ptr<FowardInfo> fwd_ptr = Search_File(fileno);
            if (fwd_ptr == nullptr)
            {
                return std::move(file_json);
            }
            file_json["title"] = fwd_ptr->_title;
            file_json["content"] = Search_Word(fwd_ptr->_content, word);
            file_json["url"] = fwd_ptr->_url;
            return std::move(file_json);
        }
    };

};